home *** CD-ROM | disk | FTP | other *** search
/ PLAYymate for OS/2 / Playmate for OS2.iso / p4os2026 / life2.c next >
Text File  |  1993-09-05  |  29KB  |  774 lines

  1. /*------------------------------------------------*/
  2. /*  LIFE2.C  --  John Conway's game of life.      */
  3. /*                                                */
  4. /*               Program from charles Petzold's   */
  5. /*               book with minor enhancements     */
  6. /*               by Larry Nomer                   */
  7. /*                                                */
  8. /*------------------------------------------------*/
  9.  
  10. #define INCL_BASE
  11. #include "life2.h"
  12.  
  13. CHAR  szClientClass [] = "Life" ;
  14. HAB   hab ;
  15. static HINI  hini ;
  16. static HWND  hwndFrame, hwndClient ;
  17.  
  18. static ULONG   ulFColor    ;
  19. static ULONG   ulBColor    ;
  20. static SHORT   sCellScale  ;
  21.  
  22. int main( void )
  23. {
  24.   static ULONG flFrameFlags = FCF_TITLEBAR       |
  25.                               FCF_SYSMENU        |
  26.                               FCF_SIZEBORDER     |
  27.                               FCF_SHELLPOSITION  |
  28.                               FCF_MINMAX         |
  29.                               FCF_TASKLIST       |
  30.                               FCF_MENU           |
  31.                               FCF_ICON           ;
  32.  
  33.   SWP          swp         ;   // Used to query window position.
  34.   ULONG        ulSize;
  35.   LONG         wx, wy, wcx, wcy;
  36.   HMQ          hmq ;
  37.   RECTL        rectlDesktop ;
  38.   QMSG         qmsg ;
  39.  
  40.   hab = WinInitialize ( 0 ) ;
  41.   hmq = WinCreateMsgQueue ( hab, 0 ) ;
  42.  
  43.   WinRegisterClass( hab, szClientClass, ClientWndProc,
  44.                                         CS_SIZEREDRAW, 0 ) ;
  45.  
  46.   hwndFrame = WinCreateStdWindow ( HWND_DESKTOP, 0L, /* Usu WS_VISIBLE here */
  47.                                    &flFrameFlags, szClientClass, NULL,
  48.                                    0L, 0UL, ID_RESOURCE, &hwndClient );
  49.  
  50.   /**************************************************************************/
  51.   /*                                                                        */
  52.   /* The entire SWP (Window Position) data structure should be in the INI   */
  53.   /* file, so it is retrieved if possible. If not, default size and         */
  54.   /* position is relative to the size of the desktop, and centred.          */
  55.   /*                                                                        */
  56.   /**************************************************************************/
  57.  
  58.   ulSize = sizeof (SWP);
  59.   if (PrfQueryProfileData (HINI_PROFILE, szAppName, szKeyPos,
  60.                            &swp, &ulSize))
  61.   {
  62.      wx = swp.x;
  63.      wy = swp.y;
  64.      wcx = swp.cx;
  65.      wcy = swp.cy;
  66.     }
  67.  else
  68.     {
  69.      WinQueryWindowRect (HWND_DESKTOP, &rectlDesktop);
  70.      wx = rectlDesktop.xRight / 8;
  71.      wy = rectlDesktop.yTop / 8;
  72.      wcx = rectlDesktop.xRight * 3 / 4;
  73.      wcy = rectlDesktop.yTop * 3 / 4;
  74.     }
  75.  
  76.  WinSetWindowPos (hwndFrame, HWND_TOP,
  77.                   wx, wy, wcx, wcy,
  78.                   SWP_SHOW    |
  79.                   SWP_SIZE    |
  80.                   SWP_MOVE    |
  81.                   SWP_ACTIVATE) ;
  82.  WinShowWindow ( hwndFrame, TRUE ) ;         /* Now make window visible.     */
  83.  
  84.  WinSetWindowText (hwndFrame, "Life2 - v1.0") ;
  85.  
  86.   while (WinGetMsg( hab, &qmsg, 0UL, 0, 0) )
  87.       WinDispatchMsg( hab, &qmsg ) ;
  88.  
  89.   WinDestroyWindow( hwndFrame );      /* Destroy window parentage     */
  90.   WinDestroyMsgQueue( hmq );          /* Cut phone line               */
  91.   WinTerminate( hab );                /* Release remaining resources  */
  92.   DosExit( EXIT_PROCESS, 0 );         /* Exit OS/2 process orderly    */
  93. }
  94.  
  95. VOID EnableMenuItem ( HWND hwndMenu, SHORT idMenuItem, BOOL fEnable )
  96. {
  97.    WinSendMsg ( hwndMenu, MM_SETITEMATTR,
  98.                 MPFROM2SHORT ( idMenuItem, TRUE ) ,
  99.                 MPFROM2SHORT ( MIA_DISABLED, fEnable ? 0 : MIA_DISABLED ) ) ;
  100. }
  101.  
  102. VOID ErrorMsg ( HWND hwnd, CHAR *szMessage )
  103. {
  104.    WinMessageBox ( HWND_DESKTOP, hwnd, szMessage, szClientClass, 0,
  105.                    MB_OK | MB_ICONEXCLAMATION ) ;
  106. }
  107.  
  108. BOOL CreateLogicalColorTable( HPS hps )
  109. {
  110. #define NALTABLEI 32L
  111.  
  112. BOOL fRet ;
  113. static LONG alTable[NALTABLEI] =
  114.                       {  0L, 0x00000000L    /* (0) black        (RGB_BLACK)  */
  115.                       ,  1L, 0x000000A0L    /* (1) dark blue                 */
  116.                       ,  2L, 0x0000A000L    /* (2) dark green                */
  117.                       ,  3L, 0x0000A0A0L    /* (3) dark cyan                 */
  118.                       ,  4L, 0x00A00000L    /* (4) dark red                  */
  119.                       ,  5L, 0x00900090L    /* (5) dark pink                 */
  120.                       ,  6L, 0x00505000L    /* (6) brown                     */
  121.                       ,  7L, 0x00D0D0D0L    /* (7) pale gray                 */
  122.                       ,  8L, 0x00505050L    /* (8) dark gray                 */
  123.                       ,  9L, 0x000000FFL    /* (9) blue         (RGB_BLUE)   */
  124.                       , 10L, 0x0000FF00L    /* (A) green        (RGB_GREEN)  */
  125.                       , 11L, 0x0000DDDDL    /* (B) cyan         (RGB_CYAN)   */
  126.                       , 12L, 0x00EE0000L    /* (C) red          (RGB_RED)    */
  127.                       , 13L, 0x00FF00FFL    /* (D) pink         (RGB_PINK)   */
  128.                       , 14L, 0x00FFFF00L    /* (E) yellow       (RGB_YELLOW) */
  129.                       , 15L, 0x00FFFFFFL    /* (F) white        (RGB_WHITE)  */
  130.                       } ;
  131.  
  132. fRet = GpiCreateLogColorTable( hps             /* PS handle                  */
  133.                              , LCOL_PURECOLOR  /* reset current color table  */
  134.                              , LCOLF_INDRGB    /* in RBG format              */
  135.                              , 0L              /* start index                */
  136.                              , NALTABLEI       /* number of table entries    */
  137.                              , alTable         /* RGB color table array      */
  138.                              ) ;
  139. return(fRet) ;
  140. }
  141.  
  142.  
  143. VOID DrawCell ( HPS hps, SHORT x, SHORT y, SHORT cxCell, SHORT cyCell,
  144.                 BYTE bCell, ULONG ulFColor, ULONG ulBColor )
  145. {
  146.    RECTL rcl ;
  147.    rcl.xLeft   = x * cxCell ;
  148.    rcl.yBottom = y * cyCell ;
  149.    rcl.xRight  = rcl.xLeft + cxCell - 1 ;
  150.    rcl.yTop    = rcl.yBottom + cyCell - 1 ;
  151.  
  152.    WinFillRect ( hps, &rcl, bCell & 1 ? ulFColor : ulBColor ) ;
  153. }
  154.  
  155. VOID DoGeneration ( HPS hps, PBYTE pbGrid, SHORT xNumCells, SHORT yNumCells,
  156.                     SHORT cxCell, SHORT cyCell,
  157.                     ULONG ulFColor, ULONG ulBColor )
  158. {
  159.    SHORT x, y, sSum ;
  160.  
  161.    for ( y = 0 ; y < yNumCells ; y++ )
  162.       for ( x = 0 ; x < xNumCells ; x++ )
  163.       {
  164.          // *** Lower left corner ***
  165.          if ( ( x == 0 ) && ( y == 0 ) )   // We're just starting.
  166.          {
  167.          // *** Examine cells we haven't visited yet.
  168.             sSum  = *(pbGrid                         + 1 ) + // Right
  169.                     *(pbGrid + xNumCells             + 1 ) + // Upper Right
  170.                     *(pbGrid +(xNumCells*y)          + 1 ) + // Lower Right
  171.                     *(pbGrid +(xNumCells*y)              ) + // Lower
  172.                     *(pbGrid +(xNumCells*(y+1))      - 1 ) + // Lower Left!
  173.                     *(pbGrid + xNumCells             - 1 ) + // Left
  174.                     *(pbGrid + xNumCells                 ) + // Upper
  175.                     *(pbGrid + (2*xNumCells)         - 1 ) ; // Upper Left
  176.          }
  177.          else
  178.          // *** Upper left corner ***
  179.          if ( ( x == 0 ) && ( y == yNumCells - 1 ) )
  180.          {
  181.             // *** Cells we have visited.
  182.             sSum = (*(pbGrid - xNumCells             ) +      // Lower
  183.                     *(pbGrid                     - 1 ) +      // Lower Left
  184.                     *(pbGrid-(xNumCells*y)           ) +      // Upper
  185.                     *(pbGrid-(xNumCells*y)       + 1 ) +      // Upper Right
  186.                     *((pbGrid-(xNumCells*(y-1))) - 1 ) +      // Upper Left
  187.                     *((pbGrid - xNumCells)       + 1 ) )      // Lower Right
  188.                                      >> 4 ;
  189.             // *** Now examine cells we haven't visited yet.
  190.             sSum += *(pbGrid                     + 1 ) +      // Right
  191.                     *(pbGrid + xNumCells         - 1 ) ;      // Left
  192.          }
  193.          else
  194.          // *** Lower right corner ***
  195.          if ( ( y == 0 ) && ( x == xNumCells - 1 ) )
  196.          {
  197.             // *** Cells we have visited.
  198.             sSum = (*(pbGrid             - 1) )              // Left
  199.                                      >> 4 ;
  200.             // *** Examine cells we haven't visited yet.
  201.             sSum += *((pbGrid - xNumCells)            + 1 ) + // Right
  202.                     *(pbGrid                          + 1 ) + // Upper Right
  203.                     *(pbGrid +(xNumCells*(y-1) )      + 1 ) + // Lower Right
  204.                     *(pbGrid +(xNumCells*y   )            ) + // Lower
  205.                     *((pbGrid +(xNumCells*y  ) )      - 1 ) + // Lower Left
  206.                     *(pbGrid + xNumCells                  ) + // Upper
  207.                     *((pbGrid + xNumCells    )        - 1 ) ; // Upper Left
  208.          }
  209.          else
  210.          // *** Upper right corner ***
  211.          if ( ( y == yNumCells - 1 ) && ( x == xNumCells - 1 ) )
  212.          {
  213.             // *** Cells we have visited (done all but current in this case).
  214.             sSum = (*(pbGrid - xNumCells             ) +      // Lower
  215.                     *((pbGrid - xNumCells)       - 1 ) +      // Lower Left
  216.                     *(pbGrid-(xNumCells*y)           ) +      // Upper
  217.                     *((pbGrid-(xNumCells*(y+1))) + 1 ) +      // Upper Right
  218.                     *((pbGrid-xNumCells*y)       - 1 ) +      // Upper Left
  219.                     *((pbGrid-xNumCells)         + 1 ) +      // Right
  220.                     *(pbGrid                     - 1 ) +      // Left
  221.                     *((pbGrid-2*xNumCells)       + 1 ) )      // Lower Right
  222.                                      >> 4 ;
  223.          }
  224.          else
  225.          // *** Bottom edge ***
  226.          if ( y == 0 )
  227.          {
  228.             // *** Cells we have visited.
  229.             sSum = (*(pbGrid             - 1) )              // Left
  230.                                      >> 4 ;
  231.             // *** Examine cells we haven't visited yet.
  232.             sSum += *(pbGrid                            + 1) + // Right
  233.                     *(pbGrid  + xNumCells               + 1) + // Upper Right
  234.                     *(pbGrid  + xNumCells*(yNumCells-1) + 1) + // Lower Right
  235.                     *(pbGrid  + xNumCells*(yNumCells-1)    ) + // Lower
  236.                     *((pbGrid + xNumCells*(yNumCells-1))- 1) + // Lower Left
  237.                     *(pbGrid  + xNumCells                  ) + // Upper
  238.                     *((pbGrid + xNumCells)              - 1) ; // Upper Left
  239.          }
  240.          else
  241.          // *** Top edge ***
  242.          if ( y == yNumCells - 1 )
  243.          {
  244.             // *** Cells we have visited.
  245.             sSum = (*(pbGrid - xNumCells             ) +      // Lower
  246.                     *((pbGrid - xNumCells)       - 1 ) +      // Lower Left
  247.                     *(pbGrid                     - 1 ) +      // Left
  248.                     *( pbGrid-(xNumCells*y)          ) +      // Upper
  249.                     *((pbGrid-(xNumCells*y))     + 1 ) +      // Upper Right
  250.                     *((pbGrid-(xNumCells*y))     - 1 ) +      // Upper Left
  251.                     *((pbGrid - xNumCells)       + 1 ) )      // Lower Right
  252.                                      >> 4 ;
  253.             // *** Examine cells we haven't visited yet.
  254.             sSum += *(pbGrid                     + 1 ) ;      // Right
  255.          }
  256.          else
  257.          // *** Left edge ***
  258.          if ( x == 0 )
  259.          {
  260.          // *** When x == 0, we haven't done the LEFT cell because it
  261.          // *** wraps to the right side of the grid.
  262.             sSum = (*(pbGrid - xNumCells    ) +      // Lower
  263.                     *(pbGrid             - 1) +      // Lower Left
  264.                     *((pbGrid - xNumCells) + 1))     // Lower Right
  265.                                      >> 4 ;
  266.          // *** Now examine cells we haven't visited yet.
  267.             sSum += *(pbGrid             + 1) +      // Right
  268.                     *(pbGrid + xNumCells + 1) +      // Upper Right
  269.                     *(pbGrid + xNumCells - 1) +      // Left  (Not done yet)
  270.                     *(pbGrid + xNumCells    ) +      // Upper
  271.                     *(pbGrid + (2*xNumCells) - 1) ;  // Upper Left
  272.          }
  273.          else
  274.          // *** Right Edge ***
  275.          if ( x == xNumCells - 1 )
  276.          {
  277.          // "Been visited group does not usually include RIGHT!  This
  278.          //  is another special effect of the grid wrap around.
  279.             sSum = (*(pbGrid                   - 1 ) +      // Left
  280.                     *((pbGrid - xNumCells)     - 1 ) +      // Lower Left
  281.                     *( pbGrid - xNumCells          ) +      // Lower
  282.                     *((pbGrid - xNumCells)     + 1 ) +      // Right
  283.                     *((pbGrid - (2*xNumCells)) + 1))        // Lower Right
  284.                                      >> 4 ;
  285.             sSum += *( pbGrid                  + 1) +       // Upper Right
  286.                     *( pbGrid + xNumCells         ) +       // Upper
  287.                     *( pbGrid + xNumCells      - 1) ;       // Upper Left
  288.          }
  289.          // *** Normal case (not on any edges or corners of grid). ***
  290.          else
  291.          {
  292.             // *** Get shifted info from cells we have already done.
  293.             sSum = (*(pbGrid               - 1) +      // Left
  294.                     *((pbGrid - xNumCells) - 1) +      // Lower Left
  295.                     *(pbGrid  - xNumCells     ) +      // Lower
  296.                     *((pbGrid - xNumCells) + 1))       // Lower Right
  297.                                      >> 4 ;
  298.             // *** and now from those not yet processed.
  299.             sSum += *(pbGrid             + 1) +        // Right
  300.                     *(pbGrid + xNumCells + 1) +        // Upper Right
  301.                     *(pbGrid + xNumCells    ) +        // Upper
  302.                     *(pbGrid + xNumCells - 1) ;        // Upper Left
  303.          }
  304.  
  305.          sSum = ( sSum | *pbGrid ) & 0x0F ;
  306.          *pbGrid <<= 4 ;
  307.          if ( sSum == 3 )
  308.             *pbGrid |= 1 ;
  309.          if (( *pbGrid & 1) != *pbGrid >> 4 )
  310.             DrawCell ( hps, x, y, cxCell, cyCell, *pbGrid,
  311.                                           ulFColor, ulBColor ) ;
  312.          pbGrid++ ;
  313.       }
  314. }
  315.  
  316. VOID DisplayGenerationNum ( HPS hps, SHORT xGen, SHORT yGen, LONG lGeneration)
  317. {
  318.    static CHAR szBuffer [24] = "Generation " ;
  319.    POINTL       ptl;
  320.  
  321.    ptl.x = xGen ;
  322.    ptl.y = yGen ;
  323.  
  324.    _ltoa ( lGeneration, szBuffer + 11, 10 ) ;
  325.  
  326.    GpiSavePS ( hps ) ;
  327.  
  328.    GpiSetBackMix ( hps, BM_OVERPAINT ) ;
  329.    GpiCharStringAt ( hps, &ptl, (LONG) strlen (szBuffer), szBuffer ) ;
  330.  
  331.    GpiRestorePS ( hps, -1L ) ;
  332. }
  333.  
  334. MRESULT EXPENTRY ClientWndProc ( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
  335. {
  336.    static BOOL    fTimerGoing ;
  337.    static HWND    hwndMenu    ;
  338.    static LONG    lGeneration ;
  339.    static PBYTE   pbBaseGrid  ;
  340.    static PBYTE   pbGrid      ;
  341.    static SHORT   cxChar, cyChar, cyDesc, cxClient, cyClient, xGenNum, yGenNum,
  342.                   cxCell, cyCell, xNumCells, yNumCells ;
  343.    static APIRET  rc          ;
  344.    static ULONG   ulTotCells  ;
  345.    static ULONG   RanPct      ;
  346.    static SHORT   cmd         ;
  347.    static BOOL    fButton1Down, fButton2Down ;
  348.           HSAVEWP hsvwp       ;
  349.           SWP     aswp[COUNT] ;
  350.    static BOOL    ctRet       ;
  351.  
  352.  
  353.           POINTL  ptlPointerPos ;
  354.  
  355.    FONTMETRICS    fm ;
  356.    HPS            hps ;
  357.    POINTL         ptl ;
  358.    SHORT          x, y ;
  359.  
  360.    switch ( msg )
  361.    {
  362.       case WM_CREATE:
  363.       {
  364.          TWOCOLORS tc ;
  365.          SCALE     sc ;
  366.          ULONG     ulSizeTC ;
  367.          ULONG     ulSizeSC ;
  368.          hps = WinGetPS ( hwnd ) ;
  369.          GpiQueryFontMetrics ( hps, (LONG) sizeof fm, &fm ) ;
  370.          cxChar = (SHORT) fm.lAveCharWidth ;
  371.          cyChar = (SHORT) fm.lMaxBaselineExt ;
  372.          cyDesc = (SHORT) fm.lMaxDescender ;
  373.          WinReleasePS ( hps ) ;
  374.          fButton1Down = fButton2Down = 0 ;
  375.          ulSizeTC = sizeof(TWOCOLORS) ;
  376.          ulSizeSC = sizeof(SCALE) ;
  377.  
  378.          hwndMenu = WinWindowFromID (
  379.                          WinQueryWindow( hwnd, QW_PARENT ),
  380.                          FID_MENU ) ;
  381.          if (PrfQueryProfileData (HINI_PROFILE, szAppName, szKeyColor,
  382.                                   &tc, &ulSizeTC ))
  383.          {
  384.              ulFColor = tc.colorF ;
  385.              ulBColor = tc.colorB ;
  386.          }
  387.          else
  388.          {
  389.              ulFColor = 2L ;
  390.              ulBColor = 7L ;
  391.          }
  392.          if (PrfQueryProfileData (HINI_PROFILE, szAppName, szKeyScale,
  393.                                   &sc, &ulSizeSC ))
  394.          {
  395.              sCellScale = sc.cScale ;
  396.          }
  397.          else
  398.          {
  399.              sCellScale = 2 ;
  400.          }
  401.          WinSendMsg ( hwndMenu, MM_SETITEMATTR,
  402.                       MPFROM2SHORT ( sCellScale, TRUE ),
  403.                       MPFROM2SHORT ( MIA_CHECKED, MIA_CHECKED ) ) ;
  404.          return 0 ;
  405.       }
  406.       case WM_SIZE:
  407.  
  408.          if (pbBaseGrid)
  409.          {
  410.             DosFreeMem ( pbBaseGrid ) ;
  411.             pbBaseGrid = 0 ;
  412.          }
  413.  
  414.          if ( fTimerGoing )
  415.          {
  416.             WinStopTimer (hab, hwnd, ID_TIMER ) ;
  417.             fTimerGoing = FALSE ;
  418.          }
  419.  
  420.          cxClient = SHORT1FROMMP ( mp2 ) ;
  421.          cyClient = SHORT2FROMMP ( mp2 ) ;
  422.  
  423.          xGenNum = cxChar ;
  424.          yGenNum = cyClient - cyChar + cyDesc ;
  425.  
  426.          cxCell = cxChar * 2 / sCellScale ;
  427.          cyCell = cyChar / sCellScale ;
  428.  
  429.          xNumCells = cxClient / cxCell ;
  430.          yNumCells = ( cyClient - cyChar ) / cyCell ;
  431.  
  432.          if ((xNumCells <= 0) || (yNumCells <= 0 ))
  433.          {
  434.             ErrorMsg ( hwnd, "Not enough room for even one cell." ) ;
  435.          }
  436.  
  437.          else if ( (LONG) xNumCells * yNumCells > 65536L )
  438.          {
  439.             ErrorMsg ( hwnd, "More than 64K cells not supported." ) ;
  440.          }
  441.  
  442.          else
  443.          {
  444.             ulTotCells = xNumCells * yNumCells ;
  445.             rc = DosAllocMem ( (PVOID *) &pbBaseGrid, ulTotCells,
  446.                                PAG_COMMIT | PAG_WRITE | PAG_READ ) ;
  447.             if ( rc != 0 )
  448.             {
  449.                 ErrorMsg ( hwnd, "Not enough memory for this many cells." ) ;
  450.                 pbBaseGrid = 0 ;
  451.                 return 0 ;
  452.             }
  453.          }
  454.  
  455.          pbGrid = pbBaseGrid ;
  456.  
  457.          for ( y = 0 ; y < yNumCells ; y++ )
  458.             for ( x = 0 ; x < xNumCells ; x++ )
  459.                *pbGrid++ = 0 ;
  460.  
  461.          WinEnableMenuItem ( hwndMenu, IDM_SIZE,   TRUE ) ;
  462.          WinEnableMenuItem ( hwndMenu, IDM_START,  pbBaseGrid != 0 ) ;
  463.          WinEnableMenuItem ( hwndMenu, IDM_STOP,   FALSE ) ;
  464.          WinEnableMenuItem ( hwndMenu, IDM_STEP,   pbBaseGrid != 0 ) ;
  465.          WinEnableMenuItem ( hwndMenu, IDM_CLEAR,  pbBaseGrid != 0 ) ;
  466.          WinEnableMenuItem ( hwndMenu, IDM_RANDOM, pbBaseGrid != 0 ) ;
  467.  
  468.          lGeneration = 0 ;
  469.          return 0 ;
  470.  
  471.       case WM_MOUSEMOVE:
  472.  
  473.          WinQueryPointerPos ( HWND_DESKTOP, &ptlPointerPos ) ;
  474.          WinMapWindowPoints ( HWND_DESKTOP, hwnd, &ptlPointerPos, 1 ) ;
  475.  
  476.          x = ptlPointerPos.x / cxCell ;
  477.          y = ptlPointerPos.y / cyCell ;
  478.  
  479.          if ( ( fButton1Down || fButton2Down ) &&
  480.               pbBaseGrid &&
  481.               !fTimerGoing &&
  482.               x < xNumCells &&
  483.               y < yNumCells )
  484.          {
  485.             pbGrid = pbBaseGrid ;
  486.  
  487.             hps = WinGetPS ( hwnd ) ;
  488.             ctRet = CreateLogicalColorTable( hps ) ;
  489.  
  490.             if ( fButton1Down )
  491.                DrawCell ( hps, x, y, cxCell, cyCell,
  492.                           ( *( pbGrid + y * xNumCells + x ) |= 1 ),
  493.                           ulFColor, ulBColor ) ;
  494.  
  495.             else if ( fButton2Down )
  496.                DrawCell ( hps, x, y, cxCell, cyCell,
  497.                           ( *( pbGrid + y * xNumCells + x ) &= 0 ),
  498.                           ulFColor, ulBColor ) ;
  499.  
  500.             WinReleasePS ( hps ) ;
  501.          }
  502.          break ;
  503.  
  504.       case WM_BUTTON1UP:
  505.          fButton1Down = 0 ;
  506.          break ;
  507.  
  508.       case WM_BUTTON2UP:
  509.          fButton2Down = 0 ;
  510.          break ;
  511.  
  512.       case WM_BUTTON1DOWN:
  513.          fButton1Down = 1 ;
  514.          break ;
  515.  
  516.       case WM_BUTTON2DOWN:
  517.          fButton2Down = 1 ;
  518.          break ;
  519.  
  520.       case WM_COMMAND:
  521.          cmd = SHORT1FROMMP( mp1 );
  522.          switch( cmd )
  523.          {
  524.             case IDM_LARGE:
  525.             case IDM_SMALL:
  526.             case IDM_TINY:
  527.             {
  528.                SCALE sc ;
  529.                WinSendMsg ( hwndMenu, MM_SETITEMATTR,
  530.                             MPFROM2SHORT ( sCellScale, TRUE ),
  531.                             MPFROM2SHORT ( MIA_CHECKED, 0   ) ) ;
  532.  
  533.                sCellScale = cmd ;
  534.  
  535.                WinSendMsg ( hwndMenu, MM_SETITEMATTR,
  536.                             MPFROM2SHORT ( sCellScale, TRUE ),
  537.                             MPFROM2SHORT ( MIA_CHECKED, MIA_CHECKED ) ) ;
  538.  
  539.                WinSendMsg ( hwnd, WM_SIZE, NULL,
  540.                             MPFROM2SHORT ( cxClient, cyClient ) ) ;
  541.  
  542.                WinInvalidateRect ( hwnd, NULL, FALSE ) ;
  543.  
  544.                sc.cScale = sCellScale ;
  545.                PrfWriteProfileData (HINI_PROFILE, szAppName, szKeyScale,
  546.                                                   &sc, sizeof (SCALE) );
  547.                return 0 ;
  548.             }
  549.             case IDM_START:
  550.  
  551.                if ( !WinStartTimer ( hab, hwnd, ID_TIMER, 1 ) )
  552.                {
  553.                   ErrorMsg ( hwnd, "Too many clocks or timers." ) ;
  554.                }
  555.                else
  556.                {
  557.                   fTimerGoing = TRUE ;
  558.                   WinEnableMenuItem ( hwndMenu, IDM_SIZE,   FALSE ) ;
  559.                   WinEnableMenuItem ( hwndMenu, IDM_START,  FALSE ) ;
  560.                   WinEnableMenuItem ( hwndMenu, IDM_STOP,   TRUE  ) ;
  561.                   WinEnableMenuItem ( hwndMenu, IDM_STEP,   FALSE ) ;
  562.                   WinEnableMenuItem ( hwndMenu, IDM_CLEAR,  FALSE ) ;
  563.                   WinEnableMenuItem ( hwndMenu, IDM_RANDOM, FALSE ) ;
  564.                }
  565.                return 0 ;
  566.  
  567.             case IDM_STOP:
  568.                WinStopTimer ( hab, hwnd, ID_TIMER ) ;
  569.                fTimerGoing = FALSE ;
  570.  
  571.                WinEnableMenuItem ( hwndMenu, IDM_SIZE,   TRUE  ) ;
  572.                WinEnableMenuItem ( hwndMenu, IDM_START,  TRUE  ) ;
  573.                WinEnableMenuItem ( hwndMenu, IDM_STOP,   FALSE ) ;
  574.                WinEnableMenuItem ( hwndMenu, IDM_STEP,   TRUE  ) ;
  575.                WinEnableMenuItem ( hwndMenu, IDM_CLEAR,  TRUE  ) ;
  576.                WinEnableMenuItem ( hwndMenu, IDM_RANDOM, TRUE  ) ;
  577.                return 0 ;
  578.  
  579.             case IDM_STEP:
  580.                WinSendMsg ( hwnd, WM_TIMER, NULL, NULL ) ;
  581.                return 0 ;
  582.  
  583.  
  584.             case IDM_CLEAR:
  585.                lGeneration = 0L ;
  586.  
  587.                pbGrid = pbBaseGrid ;
  588.  
  589.                for ( y = 0 ; y < yNumCells ; y++ )
  590.                   for ( x = 0 ; x < xNumCells ; x++ )
  591.                      *pbGrid++ = 0 ;
  592.  
  593.                WinInvalidateRect ( hwnd, NULL, FALSE ) ;
  594.  
  595.                return 0 ;
  596.  
  597.             case IDM_RAN100:
  598.             case IDM_RAN90:
  599.             case IDM_RAN80:
  600.             case IDM_RAN70:
  601.             case IDM_RAN60:
  602.             case IDM_RAN50:
  603.             case IDM_RAN40:
  604.             case IDM_RAN30:
  605.             case IDM_RAN20:
  606.             case IDM_RAN10:
  607.  
  608.                RanPct = ( cmd - 100 ) ;
  609.  
  610.                lGeneration = 0L ;
  611.  
  612.                pbGrid = pbBaseGrid ;
  613.  
  614.                for ( y = 0 ; y < yNumCells ; y++ )
  615.                   for ( x = 0 ; x < xNumCells ; x++ )
  616.                      *pbGrid++ = ( ( rand() < ( RAND_MAX * RanPct ) / 100 ) &&
  617.                                    ( x >= RAN_BORDER ) &&
  618.                                    ( x < ( xNumCells - RAN_BORDER ) ) &&
  619.                                    ( y >= RAN_BORDER ) &&
  620.                                    ( y < ( yNumCells - RAN_BORDER ) ) ) ;
  621.  
  622.                WinInvalidateRect ( hwnd, NULL, FALSE ) ;
  623.                return 0 ;
  624.  
  625.             case FCLR_BLACK:
  626.             case FCLR_DARKBLUE:
  627.             case FCLR_DARKGREEN:
  628.             case FCLR_DARKCYAN:
  629.             case FCLR_DARKRED:
  630.             case FCLR_DARKPINK:
  631.             case FCLR_BROWN:
  632.             case FCLR_PALEGRAY:
  633.             case FCLR_DARKGRAY:
  634.             case FCLR_WHITE:
  635.             case FCLR_BLUE:
  636.             case FCLR_RED:
  637.             case FCLR_GREEN:
  638.             case FCLR_CYAN:
  639.             case FCLR_PINK:
  640.             case FCLR_YELLOW:
  641.             {
  642.                TWOCOLORS tc ;
  643.                ulFColor = 100-cmd;
  644.                tc.colorF = ulFColor ;
  645.                tc.colorB = ulBColor ;
  646.                WinInvalidateRect( hwnd, (PRECTL)NULL, FALSE );
  647.                PrfWriteProfileData (HINI_PROFILE, szAppName, szKeyColor,
  648.                                                   &tc, sizeof (TWOCOLORS) );
  649.             }
  650.             break;
  651.  
  652.             case BCLR_BLACK:
  653.             case BCLR_DARKBLUE:
  654.             case BCLR_DARKGREEN:
  655.             case BCLR_DARKCYAN:
  656.             case BCLR_DARKRED:
  657.             case BCLR_DARKPINK:
  658.             case BCLR_BROWN:
  659.             case BCLR_PALEGRAY:
  660.             case BCLR_DARKGRAY:
  661.             case BCLR_WHITE:
  662.             case BCLR_BLUE:
  663.             case BCLR_RED:
  664.             case BCLR_GREEN:
  665.             case BCLR_CYAN:
  666.             case BCLR_PINK:
  667.             case BCLR_YELLOW:
  668.             {
  669.                TWOCOLORS tc ;
  670.                WinInvalidateRect( hwnd, (PRECTL)NULL, FALSE );
  671.                ulBColor = 300-cmd;
  672.                tc.colorF = ulFColor ;
  673.                tc.colorB = ulBColor ;
  674.                PrfWriteProfileData (HINI_PROFILE, szAppName, szKeyColor,
  675.                                                   &tc, sizeof (TWOCOLORS) );
  676.             }
  677.             break;
  678.          }
  679.          break;
  680.  
  681.       case WM_TIMER:
  682.  
  683.          hps = WinGetPS ( hwnd ) ;
  684.          ctRet = CreateLogicalColorTable( hps ) ;
  685.  
  686.          DisplayGenerationNum ( hps, xGenNum, yGenNum, ++lGeneration ) ;
  687.  
  688.          pbGrid = pbBaseGrid ;
  689.  
  690.          DoGeneration ( hps, pbGrid, xNumCells, yNumCells, cxCell, cyCell,
  691.                         ulFColor, ulBColor ) ;
  692.  
  693.          WinReleasePS ( hps ) ;
  694.          return 0 ;
  695.  
  696.  
  697.       case WM_PAINT:
  698.          hps = WinBeginPaint ( hwnd, NULLHANDLE, NULL ) ;
  699.          ctRet = CreateLogicalColorTable( hps ) ;
  700.          GpiErase ( hps ) ;
  701.  
  702.          if ( pbBaseGrid )
  703.          {
  704.             for ( x = 1 ; x <= xNumCells ; x++ )
  705.             {
  706.                ptl.x = cxCell * x - 1 ;
  707.                ptl.y = 0 ;
  708.                GpiMove ( hps, &ptl ) ;
  709.  
  710.                ptl.y = cyCell * yNumCells - 1 ;
  711.                GpiLine ( hps, &ptl ) ;
  712.             }
  713.  
  714.  
  715.             for ( y = 1 ; y <= yNumCells ; y++ )
  716.             {
  717.                ptl.x = 0 ;
  718.                ptl.y = cyCell * y - 1 ;
  719.                GpiMove ( hps, &ptl ) ;
  720.  
  721.  
  722.                ptl.x = cxCell * xNumCells - 1 ;
  723.                GpiLine ( hps, &ptl ) ;
  724.             }
  725.               /*  if ( *pbGrid++ )  */
  726.  
  727.             pbGrid = ( PBYTE ) pbBaseGrid ;
  728.             for ( y = 0 ; y < yNumCells ; y++ )
  729.                for ( x = 0 ; x < xNumCells ; x++ )
  730.                {
  731.                    pbGrid++ ;
  732.                    DrawCell ( hps, x, y, cxCell, cyCell,
  733.                               *( pbGrid - 1 ),
  734.                               ulFColor, ulBColor ) ;
  735.                }
  736.  
  737.             DisplayGenerationNum ( hps, xGenNum, yGenNum, lGeneration ) ;
  738.          }
  739.          WinEndPaint ( hps ) ;
  740.          return 0 ;
  741.  
  742.       case WM_DESTROY:
  743.  
  744.          if ( fTimerGoing )
  745.             WinStopTimer ( hab, hwnd, ID_TIMER ) ;
  746.  
  747.          if ( pbBaseGrid )
  748.             DosFreeMem ( pbBaseGrid ) ;
  749.  
  750.          return 0 ;
  751.  
  752.       case WM_SAVEAPPLICATION:
  753.  
  754.       /**********************************************************************/
  755.       /*                                                                    */
  756.       /* The notice to save to INI-profiles. Save current window size and   */
  757.       /* position; also save current selections of value-sets.              */
  758.       /*                                                                    */
  759.       /**********************************************************************/
  760.       {
  761.          SWP  swp;
  762.  
  763.          WinQueryWindowPos (WinQueryWindow (hwnd, QW_PARENT), &swp);
  764.          PrfWriteProfileData (HINI_PROFILE, szAppName, szKeyPos,
  765.                               &swp, sizeof (SWP));
  766.       }
  767.       break;
  768.  
  769.    }
  770.    return WinDefWindowProc ( hwnd, msg, mp1, mp2 ) ;
  771. }
  772.  
  773.  
  774.